package org.testing.dbunit.extension.xml;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

public interface ValueGetter {
    public Object get();

    public static class ValueGetterFactory {
	public static ValueGetter getter(Table schema, Field field) {
	    if (Table.class.isAssignableFrom(field.getType()))
		return new IdValueGetter(schema, field);
	    if (field.isAnnotationPresent(Temporal.class)) {
		return field.getAnnotation(Temporal.class).value()
			.equals(TemporalType.DATE) ? //
		new DateValueGetter(schema, field)
			: new TimeStampValueGetter(schema, field);
	    }
	    return new SimpleValueGetter(schema, field);
	}
    }

    public static class TimeStampValueGetter implements ValueGetter {
	private final Table schema;
	private final Field field;

	public TimeStampValueGetter(Table schema, Field field) {
	    this.schema = schema;
	    this.field = field;
	}

	public Object get() {
	    try {
		Object value = field.get(schema);
		return new SimpleDateFormat("dd-MM-yyyy hh:mm:ss")
			.format((Date) value);
	    } catch (Exception e) {
		throw new RuntimeException(e);
	    }
	}
    }

    public static class DateValueGetter implements ValueGetter {
	private final Table schema;
	private final Field field;

	public DateValueGetter(Table schema, Field field) {
	    this.schema = schema;
	    this.field = field;
	}

	public Object get() {
	    try {
		Object value = field.get(schema);
		Locale locale = Locale.getDefault();
		return new SimpleDateFormat("dd-MM-yyyy", locale)
			.format((Date) value);
	    } catch (Exception e) {
		throw new RuntimeException(e);
	    }
	}
    }

    public static class IdValueGetter implements ValueGetter {
	private static final Predicate<Field> ID = new Predicate<Field>() {
	    public boolean apply(Field field) {
		return field.isAnnotationPresent(Id.class);
	    }
	};
	private Table schema;

	public IdValueGetter(Table owner, Field field) {
	    try {
		schema = (Table) field.get(owner);
	    } catch (Exception e) {
		throw new RuntimeException(e);
	    }
	}

	public Object get() {
	    try {
		Field key = getPrimaryKey(schema);
		return key.get(schema);
	    } catch (Exception e) {
		throw new RuntimeException(e);
	    }
	}

	private Field getPrimaryKey(Table schema) {
	    return Iterables.find(Lists.newArrayList(//
		    schema.getClass().getFields()), ID);
	}
    }

    public static class SimpleValueGetter implements ValueGetter {
	private final Field field;
	private final Table schema;

	public SimpleValueGetter(Table schema, Field field) {
	    this.schema = schema;
	    this.field = field;
	}

	public Object get() {
	    try {
		field.setAccessible(true);
		return field.get(schema);
	    } catch (Exception e) {
		throw new RuntimeException(e);
	    }
	}
    }
}